﻿Imports System.Net.Sockets
Imports System.Threading
Imports System.Text

Public Class frmChat
    Private recipientName As String = ""
    Private recipientIP As String = ""
    Private chatPort As Integer = frmMain.chatPort

    'a pointer to the frmMain instance, to refer to attributes of frmMain window
    Public mainFormInstance As frmMain

    'chat related objects
    Public chatTcpClient As TcpClient
    Private networkStream As NetworkStream
    Private chatReceiveThread As Thread
    Dim keepChatting As Boolean = True

    'delegate method fr updating chat messages in the chat window
    Delegate Sub UpdateChatLogCallback(ByVal strMessage As String, ByVal personName As String)

    Public Sub New(ByRef chatTcpClnt As TcpClient, ByVal strRecName As String, ByVal strRecIP As String)
        InitializeComponent()
        Me.recipientIP = strRecIP
        Me.recipientName = strRecName
        Me.chatTcpClient = chatTcpClnt
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Text = "Chat with " & recipientName & "<" & recipientIP & ">"
        Me.KeyPreview = True
        loadChat()
        txtMessage.Focus()
    End Sub

    Private Sub loadChat()
        Try
            networkStream = chatTcpClient.GetStream()
            chatReceiveThread = New Thread(New ThreadStart(AddressOf listenToIncomingTexts))
            chatReceiveThread.Start()
        Catch ex As Exception
            MessageBox.Show("Exception Occured in loadChat ~~ " & vbCrLf & ex.ToString)
        End Try
    End Sub

    Private Sub frmChat_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
        If e.KeyCode = Keys.Escape Then
            Me.Close()
        End If
    End Sub

    Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        Try
            keepChatting = False
            networkStream.Close()
            chatTcpClient.Close()
            mainFormInstance.mapOfChatWindows.Remove(recipientIP)
        Catch ex As Exception
            Console.WriteLine("Exception Closing connections ~~ " & ex.ToString)
        End Try
    End Sub

    Private Sub sendText()
        Dim messageToSend As String = My.Settings.displayName & " says: " & txtMessage.Text.Trim()
        Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes(messageToSend)
        Try
            networkStream.Write(sendBytes, 0, sendBytes.Length)

            updateMessageLog(messageToSend, My.Settings.displayName)
            txtMessage.Text = ""
        Catch ex As Exception
            MessageBox.Show("Exception Occured!" & vbCrLf & ex.ToString)
        End Try
    End Sub

    'Private Sub chatClient_butSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSend.Click
    '    If (txtMessage.Text.Trim.Length > 0) Then
    '        sendText()
    '        txtMessage.Text = ""
    '        txtMessage.Focus()
    '    End If
    'End Sub

    ' if the user user logs off, connection might be severed. Despite that if
    ' current user wants to chat, connection needs to be re-established. Hence,
    ' before sending any text, check if connection is active - if it is severed,
    ' re-establish the connection.
    Private Sub checkIfConnectionIsActive()
        If Me.chatTcpClient.Connected Then
            Return
        Else
            Me.chatTcpClient = New TcpClient(Me.recipientIP, Me.chatPort)
            Me.networkStream = chatTcpClient.GetStream()
            keepChatting = True
            chatReceiveThread = New Thread(New ThreadStart(AddressOf listenToIncomingTexts))
            chatReceiveThread.Start()
        End If
    End Sub

    Private Sub chatClient_txtMsg_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtMessage.KeyUp
        If e.KeyCode = Keys.Enter Then
            'chatClient_butSend_Click(sender, e)
            checkIfConnectionIsActive()
            If (txtMessage.Text.Trim.Length > 0) Then
                sendText()
                txtMessage.Text = ""
                txtMessage.Focus()
            End If
        End If
    End Sub

    Private Sub listenToIncomingTexts()
        Dim sizeOfBuffer As Integer = 1024
        Dim recieveBuffer(sizeOfBuffer) As Byte
        Dim bytesRecieved As Integer
        Dim messageRecieved As String

        Try
            chatTcpClient.ReceiveBufferSize = sizeOfBuffer

            While keepChatting
                bytesRecieved = networkStream.Read(recieveBuffer, 0, CInt(chatTcpClient.ReceiveBufferSize))
                If bytesRecieved > 0 Then
                    messageRecieved = Encoding.ASCII.GetString(recieveBuffer, 0, bytesRecieved)
                    updateMessageLog(messageRecieved, recipientName)
                Else
                    Throw New Exception("Zero bytes read by chat stream. Closing session.")
                End If
            End While
        Catch ex As Exception
            keepChatting = False

            networkStream.Close()
            chatTcpClient.Close()

            mainFormInstance.mapOfChatWindows.Remove(recipientIP)
            ' if client has closed application, notify user
            If Not mainFormInstance.mapOfClients.Contains(recipientIP) Then
                finishChatSession("", "")
            End If
        End Try
    End Sub

    'append text to chat log window
    Private Sub updateMessageLog(ByVal strMessage As String, ByVal personName As String)
        If txtChatLog.InvokeRequired Then
            Dim d As New UpdateChatLogCallback(AddressOf updateMessageLog)
            Me.Invoke(d, New Object() {strMessage, personName})
        Else
            If Not txtChatLog.IsDisposed Then
                Dim currentTextLength = txtChatLog.TextLength
                txtChatLog.AppendText(strMessage + Environment.NewLine)
                txtChatLog.SelectionStart = currentTextLength
                txtChatLog.SelectionLength = (personName & " says:").Length
                If personName.Equals(My.Settings.displayName) Then
                    txtChatLog.SelectionColor = Color.Blue
                Else
                    txtChatLog.SelectionColor = Color.Crimson
                End If

                txtChatLog.SelectionFont = New Font(txtChatLog.SelectionFont, FontStyle.Bold)
                txtChatLog.SelectionLength = 0
                txtChatLog.ScrollToCaret()
            End If
        End If
    End Sub

    'disable all controls so user has no option but to close the window
    Public Sub finishChatSession(ByVal strMessage As String, ByVal personName As String)
        If txtMessage.InvokeRequired Then
            Dim d As New UpdateChatLogCallback(AddressOf finishChatSession)
            Me.Invoke(d, New Object() {strMessage, personName})
        Else
            updateMessageLog(vbCrLf & recipientName & " wont be bakaring anymore.", recipientName)
            If Not txtMessage.IsDisposed Then
                txtMessage.Enabled = False
            End If
        End If
    End Sub
End Class